home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / news / readers / nn / nn6.4.patch8 < prev    next >
Encoding:
Text File  |  1990-07-16  |  57.4 KB  |  2,188 lines

  1.          This is an official patch to nn release 6.4
  2.          -------------------------------------------
  3.  
  4.                    PATCH #8
  5.  
  6.                 Priority: HIGH
  7.  
  8.  
  9. These patches fix several bugs in the 6.4 release which introduced in
  10. patch #7 -- sigh! I really goofed that one!
  11.  
  12. - The new print-header-lines and save-header-lines variables caused
  13.   the print and save-short commands to dump core on some systems.
  14.  
  15. - It was no longer possible to map keys in the reading mode map to
  16.   simple commands, e.g. map show q next-article.
  17.  
  18. - An optimization was introduced to avoid opening the "newsgroups"
  19.   file multiple time (by not closing it) - but it was opened multiple
  20.   times anyway without closing any of them.
  21.  
  22. - It wasn't possible to set the new shading-off/-on variables as
  23.   documented in the manual.
  24.  
  25.  
  26. A couple of new features are also included in this patch (hopefully
  27. without introducing more errors this time):
  28.  
  29. - If a centralized DATA directory is used (i.e. if DB_DIRECTORY and/or
  30.   DB_DATA_DIRECTORY is defined in config.h), it will have reached a
  31.   critical size on many installations.  With this patch, the DATA
  32.   directory will be split into subdirectories with 100 groups in each
  33.   directory.
  34.  
  35.   "./inst u" will automatically split DATA into subdirectories if this
  36.   is relevant in your configuration, so you will *not* need to rebuild
  37.   the database after applying this patch!
  38.   
  39.   If for some reason you don't run "./inst u", the DATA directory is
  40.   not reorganized, but nnmaster and nn will recognize this and
  41.   continue to work as usual with the unsplit DATA directory.
  42.  
  43.   If "DB_LONG_NAMES" is defined in config.h, the DATA directory will
  44.   *not* be split (since there is no easy way to do the reorganization).
  45.   (Using long names is probably a bad idea anyway, because the
  46.   directory gets *really big* that way).
  47.  
  48. - If nnmaster terminates due to a fatal error, a message is now mailed
  49.   to OWNER (see RELEASE_NOTES for more details).
  50.  
  51. - The back_act program now takes an optional argument specifying how
  52.   many copies of the active file are maintained.  See nngoback(1) for
  53.   details.
  54.  
  55. - The "Control:" and "Sender:" header fields can now be included in
  56.   the customized headers (codes "C" and "f").
  57.  
  58. - There is a new -ZERO option to nnacct to clear all usage counters.
  59.  
  60.  
  61. As usual, all changes are described in the updated RELEASE_NOTES file
  62. (read that for more details about this patch).  Thanks to all who
  63. reported bugs and provided fixes.
  64.  
  65. To apply this patch, use nn's :patch command, or run this command from
  66. the shell in the root of the nn source tree:
  67.     patch -p0 < this-article
  68.  
  69. Then run "make all" and "./inst u".
  70.  
  71. ++Kim Storm
  72.  
  73. *** ./LAST/account.c    Mon Jul  9 17:59:52 1990
  74. --- account.c    Tue Jul 10 23:30:57 1990
  75. ***************
  76. *** 38,43 ****
  77. --- 38,44 ----
  78.   #include "config.h"
  79.   #include "options.h"
  80.   #include "proto.h"
  81. + #include <errno.h>
  82.   
  83.   import char *db_directory;
  84.   
  85. ***************
  86. *** 135,140 ****
  87. --- 136,142 ----
  88.   static int new_policy = -1;
  89.   static int new_quota = -1;
  90.   static int who_am_caller = I_AM_ACCT;
  91. + static char *zero_accounts = NULL;
  92.   
  93.   Option_Description(acct_options) {
  94.       'C', Int_Option(show_cost),
  95. ***************
  96. *** 147,152 ****
  97. --- 149,155 ----
  98.       'q', Int_Option(new_quota),
  99.       'r', Bool_Option(report),
  100.       'Q', Bool_Option(quiet),
  101. +     'Z', String_Option(zero_accounts),
  102.       '\0',
  103.   };
  104.   
  105. ***************
  106. *** 299,305 ****
  107. --- 302,353 ----
  108.       }
  109.   }
  110.   
  111. + static char *ZERO_STAMP = "(Zeroed)";
  112.   
  113. + static do_zero()
  114. + {
  115. +     FILE *old, *new;
  116. +     char *acct, bak[FILENAME];
  117. +     struct account ac;
  118. +     extern int errno;
  119. +     acct = relative(db_directory, "acct");
  120. +     old = open_file(acct, OPEN_READ);
  121. +     if (old == NULL) goto err;
  122. +     sprintf(bak, "%s.old", acct);
  123. +     if (unlink(bak) < 0 && errno != ENOENT) goto err;
  124. +     if (link(acct, bak) < 0) goto err;
  125. +     if (unlink(acct) < 0) {
  126. +     unlink(bak);
  127. +     goto err;
  128. +     }
  129. +     umask(0177);
  130. +     new = open_file(acct, OPEN_CREATE);
  131. +     if (new == NULL) goto err2;
  132. +     ac.ac_found = 0;
  133. +     strcpy(ac.ac_user, ZERO_STAMP);
  134. +     ac.ac_total = ac.ac_policy = ac.ac_quota = 0;
  135. +     time(&(ac.ac_last));
  136. +     put_entry(new, &ac);
  137. +     
  138. +     while (get_entry(old, (char *)NULL, &ac)) {
  139. +     if (strcmp(ac.ac_user, ZERO_STAMP) == 0) continue;
  140. +     ac.ac_total = 0;
  141. +     ac.ac_found = 0;
  142. +     put_entry(new, &ac);
  143. +     }
  144. +     fclose(old);
  145. +     if (fclose(new) == EOF) goto err2;
  146. +     return;
  147. +     
  148. +  err2:
  149. +     unlink(acct);
  150. +     if (link(bak, acct) == 0) unlink(bak);
  151. +  err:
  152. +     fprintf(stderr, "ZERO of accounts failed -- check permissions etc.\n");
  153. + }
  154.   
  155.   main(argc, argv)
  156.   int argc;
  157. ***************
  158. *** 317,322 ****
  159. --- 365,375 ----
  160.   
  161.       users = parse_options(argc, argv, (char *)NULL, acct_options, "");
  162.   
  163. +     if (zero_accounts && strcmp(zero_accounts, "ERO")) {
  164. +     fprintf(stderr, "Must specify -ZERO to clear accounts\n");
  165. +     exit(1);
  166. +     }
  167.       if (user_id != 0) {
  168.       if (report_all) {
  169.           fprintf(stderr, "Only root can request complete reports\n");
  170. ***************
  171. *** 330,335 ****
  172. --- 383,392 ----
  173.           fprintf(stderr, "Only root can change user quotas\n");
  174.           exit(9);
  175.       }
  176. +     if (zero_accounts) {
  177. +         fprintf(stderr, "Only root can zero user accounts\n");
  178. +         exit(9);
  179. +     }
  180.       if (users > 0) {
  181.           fprintf(stderr, "Only root can request reports for other users\n");
  182.           exit(9);
  183. ***************
  184. *** 351,357 ****
  185.       add_usage = -1;
  186.       }
  187.   
  188. !     if (add_usage > 0 || new_policy >= 0 || new_quota >= 0) {
  189.       if (acct_file) {
  190.           fprintf(stderr, "Can only update current acct file\n", acct_file);
  191.           exit(2);
  192. --- 408,414 ----
  193.       add_usage = -1;
  194.       }
  195.   
  196. !     if (zero_accounts || add_usage > 0 || new_policy >= 0 || new_quota >= 0) {
  197.       if (acct_file) {
  198.           fprintf(stderr, "Can only update current acct file\n", acct_file);
  199.           exit(2);
  200. ***************
  201. *** 358,363 ****
  202. --- 415,426 ----
  203.       }
  204.   
  205.       proto_lock(I_AM_ACCT, PL_SET_QUICK);
  206. +     }
  207. +     if (zero_accounts) {
  208. +     do_zero();
  209. +     proto_lock(I_AM_ACCT, PL_CLEAR);
  210. +     exit(0);
  211.       }
  212.   
  213.       if (acct_file) {
  214. *** ./LAST/admin.c    Mon Jun 25 15:46:36 1990
  215. --- admin.c    Thu Jul 12 13:00:15 1990
  216. ***************
  217. *** 19,24 ****
  218. --- 19,25 ----
  219.   
  220.   import char *exec_chdir_to;
  221.   
  222. + import int db_data_subdirs;
  223.   
  224.   static char *pre_input;
  225.   static int verbose = 1;
  226. ***************
  227. *** 166,171 ****
  228. --- 167,175 ----
  229.           printf("Cannot list all files (they are scattered over the news partition)\n");
  230.           return;
  231.       }
  232. +     if (db_data_subdirs)
  233. +         sprintf(command, "cd %s ; ls -l [0-9] | %s", db_data_directory, pager);
  234. +     else
  235.       sprintf(command, "ls -l %s | %s", db_data_directory, pager);
  236.       } else
  237.       sprintf(command, "ls -l %s", db_data_path(name, gh, '*'));
  238. ***************
  239. *** 896,904 ****
  240.          news_directory,
  241.          news_lib_directory);
  242.   
  243. !     printf("DB:   %s  %s\n", db_directory,
  244. !        db_data_directory != NULL ? db_data_directory :
  245. !        "(per-group files in group directories)");
  246.   
  247.       printf("ACTIVE: %s\n", news_active);
  248.   
  249. --- 900,916 ----
  250.          news_directory,
  251.          news_lib_directory);
  252.   
  253. !     printf("DB:   %s\nDATA: ", db_directory);
  254. !     if (db_data_directory != NULL) {
  255. ! #ifdef DB_LONG_NAMES
  256. !     printf("%s/{group.name}.[dx]\n", db_data_directory);
  257. ! #else
  258. !     printf("%s%s/{group-number}.[dx]\n", db_data_directory,
  259. !            db_data_subdirs ? "/[0-9]" : "");
  260. ! #endif
  261. !     } else {
  262. !     printf("%s/{group/name/path}/.nn[dx]\n", news_directory);
  263. !     }
  264.   
  265.       printf("ACTIVE: %s\n", news_active);
  266.   
  267. *** ./LAST/answer.c    Mon Jun 25 15:46:38 1990
  268. --- answer.c    Thu Jul 12 00:07:59 1990
  269. ***************
  270. *** 9,16 ****
  271. --- 9,18 ----
  272.   #include "term.h"
  273.   #include "keymap.h"
  274.   #include "options.h"
  275. + #include "regexp.h"
  276.   
  277.   extern char *temp_file;
  278. + extern char *news_lib_directory;
  279.   
  280.   export char *default_distribution = NULL;
  281.   export char *extra_mail_headers    = NULL;
  282. ***************
  283. *** 190,195 ****
  284. --- 192,268 ----
  285.       return 0;
  286.   }
  287.   
  288. + /*
  289. +  *    open_purpose_file()
  290. +  *
  291. +  *    Open "newsgroups" file once - just rewind() otherwise.
  292. +  *    Caller must NOT close it!
  293. +  */
  294. + FILE *open_purpose_file()
  295. + {
  296. +     static FILE *f = NULL;
  297. +     static int is_open = 0;
  298. +     if (is_open) {
  299. +     if (f != NULL) rewind(f);
  300. +     return f;
  301. +     }
  302. +     is_open = 1;
  303. + #ifdef NNTP
  304. +     if (use_nntp) {
  305. +     extern FILE *nntp_get_newsgroups();
  306. +     f = nntp_get_newsgroups();
  307. +     } else
  308. + #endif
  309. +     f = open_file(relative(news_lib_directory, "newsgroups"), OPEN_READ);
  310. +     return f;
  311. + }
  312. + /*
  313. +  * display list of group purposes
  314. +  */
  315. + display_group_list(get_regexp)
  316. + int get_regexp;
  317. + {
  318. +     FILE *f;
  319. +     char line[512];
  320. +     extern regexp *pg_regexp;
  321. +     extern int pg_new_regexp;
  322. +     char *expr = NULL;
  323. +     if (get_regexp) {
  324. +     prompt("\1/\1");
  325. +     expr = get_s((char *)NULL, (char *)NULL, (char *)NULL, NULL_FCT);
  326. +     if (expr == NULL || *expr == NUL) return 0;
  327. +     }
  328. +     if ((f = open_purpose_file()) == NULL) return 0;
  329. +     if (who_am_i == I_AM_POST) {
  330. +     gotoxy(0, prompt_line + 1);
  331. +     clrpage(prompt_line + 1);
  332. +     pg_init(prompt_line + 1, 1);
  333. +     } else {
  334. +     home();
  335. +     clrdisp();
  336. +     pg_init(0, 1);
  337. +     }
  338. +     pg_regexp = regcomp(expr);
  339. +     while (fgets(line, 512, f)) {
  340. +     if (pg_regexp && regexec_fold(pg_regexp, line) == 0) continue;
  341. +     if (pg_next() < 0) break;
  342. +     if (pg_new_regexp) {
  343. +         pg_new_regexp = 0;    /* pg_next has cleared display */
  344. +         rewind(f);
  345. +         continue;
  346. +     }
  347. +     printf("%s", line);
  348. +     }
  349. +     return 1;
  350. + }
  351.   
  352.   /*
  353.    * invoke aux shell script with suitable arguments
  354. ***************
  355. *** 735,741 ****
  356.       raw();
  357.       clrdisp();
  358.       prompt_line = 0;
  359. !     post_menu();
  360.       putchar(CR); putchar(NL);
  361.       unset_raw();
  362.       if (*delayed_msg)
  363. --- 808,814 ----
  364.       raw();
  365.       clrdisp();
  366.       prompt_line = 0;
  367. !     if (post_menu() == 2) clrdisp();
  368.       putchar(CR); putchar(NL);
  369.       unset_raw();
  370.       if (*delayed_msg)
  371. ***************
  372. *** 748,753 ****
  373. --- 821,829 ----
  374.   {
  375.       register FILE *t, *src;
  376.       register int c;
  377. +     int must_redraw = 0;
  378. +     char brk_chars[4];
  379. +     extern key_type help_key;
  380.       char *str, *tail;
  381.       char group_name[FILENAME], subject[FILENAME],
  382.        distribution[FILENAME], keywords[FILENAME], summary[FILENAME];
  383. ***************
  384. *** 770,781 ****
  385.   
  386.       prompt(who_am_i == I_AM_POST ? "Newsgroups: " : "\1POST to group\1 ");
  387.   
  388.       str = get_s(current_group ? current_group->group_name : NONE,
  389. !             group_name, NONE, group_completion);
  390. !     if (str == NULL) return 0;
  391.       if (*str == NUL) {
  392.           if (current_group == NULL || (current_group->group_flag & G_FAKED))
  393. !         return 0;
  394.           str = current_group->group_name;
  395.       }
  396.       strcpy(group_name, str);
  397. --- 846,866 ----
  398.   
  399.       prompt(who_am_i == I_AM_POST ? "Newsgroups: " : "\1POST to group\1 ");
  400.   
  401. +     strcpy(brk_chars, "??/");
  402. +     brk_chars[0] = help_key;
  403.       str = get_s(current_group ? current_group->group_name : NONE,
  404. !             group_name, brk_chars, group_completion);
  405. !     if (str == NULL) goto no_post;
  406. !     if (*str == '?' || (key_type)(*str) == help_key || *str == '/') {
  407. !         if (display_group_list(*str == '/'))
  408. !         must_redraw = 2;
  409. !         else
  410. !         msg("No group list is avaialbe");
  411. !         goto again_group;
  412. !     }
  413.       if (*str == NUL) {
  414.           if (current_group == NULL || (current_group->group_flag & G_FAKED))
  415. !         goto no_post;
  416.           str = current_group->group_name;
  417.       }
  418.       strcpy(group_name, str);
  419. ***************
  420. *** 792,804 ****
  421.   
  422.           if (tail) *tail++ = ',';
  423.       }
  424. !     if (who_am_i == I_AM_POST) prompt_line++;
  425.       }
  426.   
  427.       if ((str = post_subject) == NULL) {
  428.       prompt("Subject: ");
  429.       str = get_s(NONE, NONE, NONE, NULL_FCT);
  430. !     if (str == NULL || *str == NUL) return 0;
  431.       if (who_am_i == I_AM_POST) prompt_line++;
  432.       }
  433.       strcpy(subject, str);
  434. --- 877,896 ----
  435.   
  436.           if (tail) *tail++ = ',';
  437.       }
  438. !     if (who_am_i == I_AM_POST) {
  439. !         prompt_line++;
  440. !         if (must_redraw) {
  441. !         gotoxy(0, prompt_line);
  442. !         clrpage(prompt_line);
  443. !         must_redraw = 1;
  444. !         }
  445. !     }
  446.       }
  447.   
  448.       if ((str = post_subject) == NULL) {
  449.       prompt("Subject: ");
  450.       str = get_s(NONE, NONE, NONE, NULL_FCT);
  451. !     if (str == NULL || *str == NUL) goto no_post;
  452.       if (who_am_i == I_AM_POST) prompt_line++;
  453.       }
  454.       strcpy(subject, str);
  455. ***************
  456. *** 806,812 ****
  457.       if ((str = post_keywords) == NULL) {
  458.       prompt("Keywords: ");
  459.       str = get_s(NONE, NONE, NONE, NULL_FCT);
  460. !     if (str == NULL) return 0;
  461.       if (who_am_i == I_AM_POST) prompt_line++;
  462.       }
  463.       strcpy(keywords, str);
  464. --- 898,904 ----
  465.       if ((str = post_keywords) == NULL) {
  466.       prompt("Keywords: ");
  467.       str = get_s(NONE, NONE, NONE, NULL_FCT);
  468. !     if (str == NULL) goto no_post;
  469.       if (who_am_i == I_AM_POST) prompt_line++;
  470.       }
  471.       strcpy(keywords, str);
  472. ***************
  473. *** 814,820 ****
  474.       if ((str = post_summary) == NULL) {
  475.       prompt("Summary: ");
  476.       str = get_s(NONE, NONE, NONE, NULL_FCT);
  477. !     if (str == NULL) return 0;
  478.       if (who_am_i == I_AM_POST) prompt_line++;
  479.       }
  480.       strcpy(summary, str);
  481. --- 906,912 ----
  482.       if ((str = post_summary) == NULL) {
  483.       prompt("Summary: ");
  484.       str = get_s(NONE, NONE, NONE, NULL_FCT);
  485. !     if (str == NULL) goto no_post;
  486.       if (who_am_i == I_AM_POST) prompt_line++;
  487.       }
  488.       strcpy(summary, str);
  489. ***************
  490. *** 832,838 ****
  491.   
  492.       prompt("Distribution: (default '%s') ", distribution);
  493.       str = get_s(NONE, NONE, NONE, NULL_FCT);
  494. !     if (str == NULL) return 0;
  495.       if (*str) strcpy(distribution, str);
  496.       if (who_am_i == I_AM_POST) prompt_line++;
  497.       }
  498. --- 924,930 ----
  499.   
  500.       prompt("Distribution: (default '%s') ", distribution);
  501.       str = get_s(NONE, NONE, NONE, NULL_FCT);
  502. !     if (str == NULL) goto no_post;
  503.       if (*str) strcpy(distribution, str);
  504.       if (who_am_i == I_AM_POST) prompt_line++;
  505.       }
  506. ***************
  507. *** 840,846 ****
  508.       new_temp_file();
  509.       if ((t = open_file(temp_file, OPEN_CREATE)) == NULL) {
  510.       msg("Can't create %s", temp_file);
  511. !     return 0;
  512.       }
  513.   
  514.       if (!post_no_edit)
  515. --- 932,938 ----
  516.       new_temp_file();
  517.       if ((t = open_file(temp_file, OPEN_CREATE)) == NULL) {
  518.       msg("Can't create %s", temp_file);
  519. !     goto no_post;
  520.       }
  521.   
  522.       if (!post_no_edit)
  523. ***************
  524. *** 877,884 ****
  525.       aux_sh((article_header *)NULL, news_script, "post",
  526.          post_no_edit ? "send" : "edit", news_record,
  527.          "Article%s posted", append_sig_post);
  528.   
  529. !     return 1;
  530.   }
  531.   
  532.   init_answer()
  533. --- 969,978 ----
  534.       aux_sh((article_header *)NULL, news_script, "post",
  535.          post_no_edit ? "send" : "edit", news_record,
  536.          "Article%s posted", append_sig_post);
  537. +     must_redraw = 1;
  538.   
  539. !  no_post:
  540. !     return must_redraw;
  541.   }
  542.   
  543.   init_answer()
  544. *** ./LAST/back_act.sh    Sat Mar 31 23:12:40 1990
  545. --- back_act.sh    Tue Jul 10 21:41:27 1990
  546. ***************
  547. *** 8,20 ****
  548.   #    It should be invoked by cron every day at midnight.
  549.   #    It should run as user `news'!
  550.   #
  551.   
  552.   cd $DB || exit 1
  553.   
  554. ! p=15
  555.   l=""
  556. ! for i in 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
  557.   do
  558.       if [ -f active.$i ]
  559.       then
  560.           mv active.$i active.$p
  561. --- 8,23 ----
  562.   #    It should be invoked by cron every day at midnight.
  563.   #    It should run as user `news'!
  564.   #
  565. + #    Call:  back_act [days]
  566. + #    Default: keep copy of active file for the last 14 days.
  567.   
  568.   cd $DB || exit 1
  569.   
  570. ! p=${1-15}
  571.   l=""
  572. ! while [ "$p" -gt 0 ]
  573.   do
  574. +     i="`expr $p - 1`"
  575.       if [ -f active.$i ]
  576.       then
  577.           mv active.$i active.$p
  578. *** ./LAST/db.c    Tue May 22 12:53:36 1990
  579. --- db.c    Thu Jul 12 20:53:59 1990
  580. ***************
  581. *** 10,15 ****
  582. --- 10,16 ----
  583.   
  584.   import char
  585.       *master_directory, *db_directory, *db_data_directory, *news_directory;
  586. + import int db_data_subdirs;
  587.   
  588.   export master_header master;
  589.   export group_header *active_groups = NULL;
  590. ***************
  591. *** 530,535 ****
  592. --- 531,540 ----
  593.   #ifdef DB_LONG_NAMES
  594.       sprintf(namebuf, "%s/%s.%c", db_data_directory, gh->group_name, d_or_x);
  595.   #else
  596. +     if (db_data_subdirs)
  597. +         sprintf(namebuf, "%s/%d/%d.%c", db_data_directory, 
  598. +             gh->group_num/100, gh->group_num, d_or_x);
  599. +     else
  600.       sprintf(namebuf, "%s/%d.%c", db_data_directory, gh->group_num, d_or_x);
  601.   #endif
  602.       } else {
  603. ***************
  604. *** 569,576 ****
  605.           log_entry('E', "Cannot unlink %s (errno=%d)", data_file, errno);
  606.       f = NULL;
  607.       } else {
  608.       f = open_file(data_file, (mode & ~MUST_EXIST));
  609. !     if (f == NULL && (mode & MUST_EXIST))
  610.           sys_error("%s (%d): cannot open '%c' file (mode=%x, errno=%d)",
  611.                 gh->group_name, (int)(gh->group_num), d_or_x,
  612.                 mode, errno);
  613. --- 574,599 ----
  614.           log_entry('E', "Cannot unlink %s (errno=%d)", data_file, errno);
  615.       f = NULL;
  616.       } else {
  617. +      again:
  618.       f = open_file(data_file, (mode & ~MUST_EXIST));
  619. !     if (f != NULL) return f;
  620. ! #ifndef DB_LONG_NAMES
  621. !     if (db_data_subdirs && (mode&0xf) == OPEN_CREATE && errno == ENOENT) {
  622. !         char *s;
  623. !         s = strrchr(data_file, '/');
  624. !         *s = NUL;
  625. !         if (!file_exist(data_file, "dx")) {
  626. !         if (mkdir(data_file, 0755) < 0)
  627. !             sys_error("Cannot create directory %s", data_file);
  628. !         log_entry('C', "Created directory %s", data_file);
  629. !         *s = '/';
  630. !         goto again;
  631. !         }
  632. !         *s = '/';
  633. !         errno = ENOENT;
  634. !     }
  635. ! #endif
  636. !     if (mode & MUST_EXIST)
  637.           sys_error("%s (%d): cannot open '%c' file (mode=%x, errno=%d)",
  638.                 gh->group_name, (int)(gh->group_num), d_or_x,
  639.                 mode, errno);
  640. *** ./LAST/doc/INSTALLATION    Tue Jun 12 11:46:29 1990
  641. --- doc/INSTALLATION    Wed Jul 11 21:58:10 1990
  642. ***************
  643. *** 550,556 ****
  644. --- 550,563 ----
  645.   or in /usr/lib/crontab (BSD):
  646.       00 23 * * * su - news MASTER_DIRECTORY/back_act
  647.   
  648. + The default setup allows you to go 14 days back with nngoback, but if
  649. + you don't keep news that long, there is no reason to keep that many
  650. + copies of the active file either.  In that case, you can supply the
  651. + maximum number of days as an argument to back_act.  Of course, you can
  652. + also keep more than 14 copies of the active file to allow nngoback to
  653. + go more than 14 days back by giving back_act an argument larger than 14.
  654.   
  655.         STEP 7.4:  PREPARE FOR NNSPEW TO BE RUN REGULARLY
  656.         -------------------------------------------------
  657.   
  658. ***************
  659. *** 602,607 ****
  660. --- 609,620 ----
  661.       nnmail is a supported part of nn - if you really want to
  662.       run a domain-based mailer, get smail 2.5 or later.  And if you
  663.       ask me how to use it I will answer: "Get SMAIL instead".
  664. + CLIENT_DIRECTORY/motd
  665. +     Every time you change this file, it will be shown to the nn
  666. +     users the next time they invoke nn.  This can be used to
  667. +     inform the users about changes in the news environment or
  668. +     local policies.  (motd = message of the day)
  669.   
  670.   
  671.   NNTP_SERVER
  672. *** ./LAST/doc/RELEASE_NOTES    Mon Jul  9 17:59:53 1990
  673. --- doc/RELEASE_NOTES    Mon Jul 16 12:01:59 1990
  674. ***************
  675. *** 924,929 ****
  676. --- 924,972 ----
  677.   From:    conrad@zeno.mmwb.ucsf.EDU (Conrad Huang)
  678.   Fixed:    Patch #7 [folder.c]
  679.   
  680. + Prog:    nn
  681. + Title:    :print (print-header-type 1) & save-short dumps core [patch #7]
  682. + From:    Mark Nagel <nagel@beaver.ICS.UCI.EDU>
  683. +     ianh@bhpmrl.oz.au (Ian Hoyle)
  684. + Fixed:    Patch #8 [save.c]
  685. + Prog:    nn
  686. + Title:    "map show" cannot bind simple commands [patch #7]
  687. + From:    Jaap Vermeulen <jaap%sequent@relay.EU.net>
  688. + Fixed:    Patch #8 [init.c keymap.c]
  689. + Prog:    nn
  690. + Title:    "newsgroups" file is reopened for each group (w/o close) [patch #7]
  691. + From:    KFS
  692. + Fixed:    Patch #8 [answer.c menu.c]
  693. +     A bug introduced by an attempted optimization in patch #7 - GRRR!
  694. + Prgog:    nn
  695. + Title:    shading-on/-off variables could not be set as documented [patch #7]
  696. + From:    dean@coplex.UUCP (Dean Brooks)
  697. + Fixed:    Patch #8 [variable.c]
  698. + Prog:    nn
  699. + Title:    Cannot define multi-keys #n interactively anymore [patch #7]
  700. + From:    marius@rhi.hi.is (Marius Olafsson)
  701. + Fixed:    Patch #8 [init.c]
  702. + Prog:    nn -g
  703. + Title:    Answer '+' to nn -g prompt gives core dump.
  704. + From:    miller@SCTC.COM (Steven M. Miller)
  705. + Fixed:    Patch #8 [group.c]
  706. + Prog:    nn
  707. + Title:    Cancelled folder articles are counted wrong if cancel attr is cleared
  708. + From:    Stephen Bellantoni <sjb@cs.toronto.edu>
  709. + Fixed:    Patch #8 [folder.c menu.c]
  710. + Prog:    nn
  711. + Title:    Crashes when doing G% in (groups with long names)
  712. + From:    Tony Wilson <wilson@issun3.stc.nl>
  713. + Fixed:    Patch #8 [menu.c  -- buffer overrun in get_purpose]
  714.   
  715.   New features since initial 6.4.0 release
  716.   ----------------------------------------
  717. ***************
  718. *** 1135,1137 ****
  719. --- 1178,1254 ----
  720.   Title:    New interactive :load command to reload init file (sequence is ignored)
  721.   From:    KFS
  722.   Added:    Patch #7 [init.c]
  723. + Prog:    nn, nnmaster - database organization
  724. + Title:    DB_DATA is split into subdirs with 100 groups each.
  725. + From:    KFS on request from Scott Merrilees <Sm@soafy.bhpese.oz.au>
  726. + Added:    Patch #8 [admin.c db.c global.c inst.sh prefix.c]
  727. +     See the header of patch 8 for more info.  Many thanks to Scott
  728. +     for helping me testing the patch.
  729. + Prog:    nnmaster
  730. + Title:    nnmaster fatal errors are now mailed to OWNER
  731. + From:    KFS on several requests
  732. + Added:    Patch #8 [global.c]
  733. +     The error message is piped into the following command (default):
  734. +         $MAILX -s 'nnmaster fatal error' $OWNER
  735. +     If FATAL_ERROR_MAIL_CMD is defined it will be used instead, e.g.
  736. +         #define FATAL_ERROR_MAIL_CMD "/bin/mail storm"
  737. + Prog:    back_act
  738. + Title:    Number of active copies to keep can now be specified as argument.
  739. + From:    KFS on request from Bruce Fisher <bruce@egh-qc.co.uk>
  740. + Added:    Patch #8 [back_act.sh nngoback.1 INSTALLATION]
  741. +     If news is expired after 7 days, there is no use for 14 days of
  742. +     active files.  This can now be reflected in the back_act call:
  743. +         $MASTER/back_act 7
  744. + Prog:    nn
  745. + Title:    Control: (C) and Sender: (f) fields can now be included in header-lines
  746. + From:    KFS on request from karl@ttank.com (Karl Bunch)
  747. + Added:    Patch #8 [news.h news.c more.c nn.1]
  748. + Prog:    nnacct -ZERO
  749. + Title:    New -ZERO option to clear the usage count for all users.
  750. + From:    KFS on request from Claus Dr{by <cld@dkuug.dk>
  751. + Added:    Patch #8 [account.c nnacct.1m]
  752. + Prog:    nn, nnpost
  753. + Title:    nnpost and :post will show newsgroups file if ? is entered
  754. + From:    KFS on request from andrew@cphmlsk.resam.dk (Leif Andrew Rump)
  755. + Added:    Patch #8 [answer.c term.c]
  756. +     If '?' is entered to the "Newsgroup:" prompt, a list of groups
  757. +     and their purpose is displayed.
  758. +     It is possible to search for entries contaning a specific word
  759. +     (really any regular expression), by entering '/expr' to the
  760. +     "Hit any key to continue" prompt.
  761. + Prog:    nn
  762. + Title:    A "message of the day" file is now displayed whenever it changes.
  763. + From:    KFS on request from asd@mace.cc.purdue.edu (Kareth)
  764. + Added:    Patch #8 [nn.c init.c term.c variable.c INSTALLATION nn.1]
  765. +     The file CLIENT/motd will be shown when users start nn and the file
  766. +     has changed since they last started nn.  They can inhibit showing
  767. +     the motd file by unsetting the variable "motd", and the file can be
  768. +     shown at any time using the command ":motd".
  769. + Prog:    nn
  770. + Title:    Will now always check .rnlast or age of .newsrc if .nn/LAST not found.
  771. + From:    KFS on request from Peter Andersen
  772. + Added:    Patch #8 [newsrc.c]
  773. +     Converts from other news readers (e.g. rn) will have a .rnlast or at
  774. +     least a .newsrc file which can be used to guess which groups are NEW.
  775. +     This information is now used even when new-groups-action == 3 or 4
  776. +     for new nn users (without a LAST file); before all groups not in
  777. +     .newsrc would be seen as NEW groups.
  778. + Prog:    nn
  779. + Title:    The proper "re prefix" is now also shown on the "*" header in read mode
  780. + From:    Uwe Doering (adapted by KFS)
  781. + Added:    Patch #8 [menu.c more.c variable.c]
  782. *** ./LAST/folder.c    Mon Jul  9 17:59:53 1990
  783. --- folder.c    Wed Jul 11 19:31:56 1990
  784. ***************
  785. *** 230,236 ****
  786.   {
  787.       if (ah->attr == A_CANCEL) {
  788.       cancel_count--;
  789. !     ah->flag = 0;
  790.       } else {
  791.       cancel_count++;
  792.       ah->attr = A_CANCEL;
  793. --- 230,236 ----
  794.   {
  795.       if (ah->attr == A_CANCEL) {
  796.       cancel_count--;
  797. !     ah->attr = 0;
  798.       } else {
  799.       cancel_count++;
  800.       ah->attr = A_CANCEL;
  801. ***************
  802. *** 371,376 ****
  803. --- 371,384 ----
  804.        reenter_menu:
  805.       menu_cmd = menu(folder_header);
  806.   
  807. +     if (mode == 0 && cancel_count) {
  808. +         register article_number cur;
  809. +         cancel_count = 0;
  810. +         for (cur = 0; cur < n_articles; cur++) {
  811. +         if (articles[cur]->attr == A_CANCEL) cancel_count++;
  812. +         }
  813. +     }
  814.       if (mode == 0 && cancel_count) {
  815.           clrdisp();
  816.           printf("Folder: %s\nFile:   %s\n\n", folder_name, folder_file);
  817. *** ./LAST/global.c    Mon Jun 25 15:46:40 1990
  818. --- global.c    Thu Jul 12 19:45:15 1990
  819. ***************
  820. *** 21,26 ****
  821. --- 21,27 ----
  822.   
  823.   export char *db_directory;    /* /usr/spool/nn or NEWS_DIR/.nn */
  824.   export char *db_data_directory;    /* ..../DATA     or undefined    */
  825. + export int db_data_subdirs = 0;    /* set if DATA/[0-9]/ exist     */
  826.   
  827.   export char *news_active;    /* NLIB/active or DB/ACTIVE */
  828.   
  829. ***************
  830. *** 213,218 ****
  831. --- 214,223 ----
  832.       db_data_directory = NULL;
  833.   #endif
  834.   #endif
  835. + #ifndef DB_LONG_NAMES
  836. +     if (db_data_directory != NULL)
  837. +     db_data_subdirs = file_exist(relative(db_data_directory, "0"), "dx");
  838. + #endif
  839.   
  840.   #ifdef NEWS_LIB_DIRECTORY
  841.       news_lib_directory = NEWS_LIB_DIRECTORY;
  842. ***************
  843. *** 526,531 ****
  844. --- 531,556 ----
  845.       return 1;
  846.   }
  847.   
  848. + static mail_sys_error(err)
  849. + char *err;
  850. + {
  851. +     FILE *f;
  852. +     char cmd[FILENAME*2];
  853. + #ifdef FATAL_ERROR_MAIL_CMD
  854. +     strcpy(cmd, FATAL_ERROR_MAIL_CMD);
  855. + #else
  856. + #ifdef MAILX
  857. +     sprintf(cmd, "%s -s 'nnmaster fatal error' %s", MAILX, OWNER);
  858. + #else
  859. +     sprintf(cmd, "mail %s", OWNER);
  860. + #endif
  861. + #endif
  862. +     if ((f = popen(cmd, "w")) == NULL) return;
  863. +     fprintf(f, "nnmaster terminated\n\nFatal system error:\n%s\n", err);
  864. +     pclose(f);
  865. + }
  866.   /*VARARGS*/
  867.   sys_error(va_alist)
  868.   va_dcl
  869. ***************
  870. *** 545,550 ****
  871. --- 570,576 ----
  872.       end_vararg;
  873.   
  874.       if (who_am_i == I_AM_MASTER) {
  875. +     mail_sys_error(buf);
  876.       if (dont_write_console) nn_exit(7);
  877.   #ifndef HAVE_SYSLOG
  878.       f = open_file("/dev/console", OPEN_CREATE);
  879. *** ./LAST/group.c    Mon Jul  9 17:59:55 1990
  880. --- group.c    Tue Jul 10 23:15:26 1990
  881. ***************
  882. *** 767,773 ****
  883.       m_endinput();
  884.       if (strcmp(answer, "+") == 0)
  885.       answer = (gh && gh->save_file != NULL) ? gh->save_file : 
  886. !         (gh->group_flag & G_FOLDER) ? folder_save_file : default_save_file;
  887.       menu_cmd = folder_menu(answer, 0);
  888.       gh = NULL;
  889.       goto goto_exit;
  890. --- 767,774 ----
  891.       m_endinput();
  892.       if (strcmp(answer, "+") == 0)
  893.       answer = (gh && gh->save_file != NULL) ? gh->save_file : 
  894. !              (gh && gh->group_flag & G_FOLDER) ? folder_save_file :
  895. !          default_save_file;
  896.       menu_cmd = folder_menu(answer, 0);
  897.       gh = NULL;
  898.       goto goto_exit;
  899. *** ./LAST/init.c    Mon Jul  9 17:59:55 1990
  900. --- init.c    Thu Jul 12 21:58:36 1990
  901. ***************
  902. *** 245,250 ****
  903. --- 245,251 ----
  904.       "man",            3,    0,
  905.       "map",            3,    4,
  906.       "mkdir",            5,    1,
  907. +     "motd",            4,    0,
  908.       "patch",            5,    0, /* QUICK HACK */
  909.       "post",            4,    0, /* QUICK HACK */
  910.       "print",            5,    -3, /* QUICK HACK */
  911. ***************
  912. *** 362,367 ****
  913. --- 363,369 ----
  914.   
  915.            case 4:
  916.           /* [map XXX ]Y command[ N] */
  917. +         if (*head == '#') return -1;
  918.           for (p = head, temp = 0; *p; )
  919.               if (*p++ == SP) {
  920.               while (*p && *p == SP) p++;
  921. ***************
  922. *** 682,688 ****
  923.       }
  924.       
  925.       if (code == K_UNBOUND && argv(3))
  926. !     code = lookup_command(argv(3), K_ONLY_MENU);
  927.       
  928.       switch (code) {
  929.        case K_EQUAL_KEY:
  930. --- 684,690 ----
  931.       }
  932.       
  933.       if (code == K_UNBOUND && argv(3))
  934. !     code = lookup_command(argv(3), map_def->km_flag & (K_ONLY_MENU|K_ONLY_MORE));
  935.       
  936.       switch (code) {
  937.        case K_EQUAL_KEY:
  938. ***************
  939. *** 1059,1064 ****
  940. --- 1061,1072 ----
  941.           init_group(orig_group);
  942.   
  943.           return AC_REDRAW;
  944. +     }
  945. +     CASE( "motd" ) {
  946. +         if (display_motd(0)) return AC_REDRAW;
  947. +         msg("no motd file");
  948. +         break;
  949.       }
  950.   
  951.       CASE( "sort" ) {
  952. *** ./LAST/inst.sh    Tue Jun 12 11:46:37 1990
  953. --- inst.sh    Mon Jul 16 15:52:45 1990
  954. ***************
  955. *** 156,161 ****
  956. --- 156,168 ----
  957.       then
  958.           OPT="$OPT master"
  959.       fi
  960. +     if $DBSHORTNAME
  961. +     then
  962. +         if [ -n "$DBDATA" -a -d "$DBDATA" -a ! -d "$DBDATA/0" ]
  963. +         then
  964. +             OPT="$OPT splitdb"
  965. +         fi
  966. +     fi
  967.       if [ -f "$BIN/nn" ]
  968.       then
  969.           OPT="$OPT bin"
  970. ***************
  971. *** 367,376 ****
  972.       ) &
  973.       ;;
  974.   
  975.   init)
  976.       echo
  977.       ./inst mkdir "$DB" 755 || exit 1
  978. !     ./inst mkdir "$DBDATA" 755 || exit 1
  979.   
  980.       if $NNTP ; then
  981.           if [ x"$NNTPCACHE" != "x" ] ; then
  982. --- 374,446 ----
  983.       ) &
  984.       ;;
  985.   
  986. + splitdb)
  987. +     (
  988. +     echo
  989. +     echo "Rearranging $DBDATA directory for better performance."
  990. +     echo "Notice:  If interrupted, the database must be rebuilt!"
  991. +     echo "Be patient.  This may take a while...."
  992. +     echo
  993. +     $MASTER/nnmaster -l "DATABASE UPGRADE IN PROGRESS"
  994. +     OLDDB="${DBDATA}-old"
  995. +     mv ${DBDATA} ${OLDDB} || exit 1
  996. +     ./inst mkdir "$DBDATA" 755 || exit 1
  997. +     Ngrp="`cat ${DB}/GROUPS | wc -l`"
  998. +     Ngrp="`expr $Ngrp + 1`"
  999. +     Ndir="`expr $Ngrp / 100`"
  1000. +     i=0
  1001. +     while [ $i -le $Ndir ]
  1002. +     do
  1003. +         ./inst mkdir "${DBDATA}/${i}" 755 || exit 1
  1004. +         i="`expr $i + 1`"
  1005. +     done
  1006. +     cd ${OLDDB}
  1007. +     i=$Ndir
  1008. +     while [ $i -ge 1 ]
  1009. +     do
  1010. +         if [ "`echo ${i}[0-9][0-9].[dx]`" != "${i}[0-9][0-9].[dx]" ]
  1011. +         then
  1012. +             echo "Moving groups ${i}00-${i}99 to ${DBDATA}/${i}"
  1013. +             mv ${i}[0-9][0-9].[dx] ${DBDATA}/${i}
  1014. +         fi
  1015. +         i="`expr $i - 1`"
  1016. +     done
  1017. +     if [ "`echo *.[dx]`" != '*.[dx]' ]
  1018. +     then
  1019. +         echo "Moving groups 0-99 to ${DBDATA}/${i}"
  1020. +         mv *.[dx] ${DBDATA}/0
  1021. +     fi
  1022. +     cd /tmp
  1023. +     rm -r ${OLDDB}
  1024. +     $MASTER/nnmaster -l
  1025. +     echo "Database reorganization completed."
  1026. +     echo
  1027. +     )
  1028. +     ;;
  1029.   init)
  1030.       echo
  1031.       ./inst mkdir "$DB" 755 || exit 1
  1032. !     if [ -n "$DBDATA" ] ; then
  1033. !         if [ -d "$DBDATA" -a "$DBDATA" = "${DB}/DATA" ]
  1034. !         then
  1035. !             echo "Removing old data files"
  1036. !             ( cd /tmp && rm -r "$DBDATA" )
  1037. !         fi
  1038. !         ./inst mkdir "$DBDATA" 755 || exit 1
  1039. !         if $DBSHORTNAME
  1040. !         then
  1041. !             ./inst mkdir "$DBDATA/0" 755 || exit 1
  1042. !         fi
  1043. !     fi
  1044.   
  1045.       if $NNTP ; then
  1046.           if [ x"$NNTPCACHE" != "x" ] ; then
  1047. ***************
  1048. *** 418,423 ****
  1049. --- 488,494 ----
  1050.   expired soon anyway.  A value in the range 100-500 should be more than
  1051.   enough.  If you don't specify a limit, all articles will be collected,
  1052.   but it may take quite some time if the min fields are unreliable.
  1053.   EOF
  1054.       fi
  1055.   
  1056. *** ./LAST/keymap.c    Mon Jul  9 17:59:56 1990
  1057. --- keymap.c    Wed Jul 11 00:04:59 1990
  1058. ***************
  1059. *** 577,586 ****
  1060.       if (t < 0)
  1061.           j = k-1;
  1062.       else {
  1063. !         if (cnmp->cmd_restriction == 0
  1064. !         || (cnmp->cmd_restriction & restriction))
  1065. !         return cnmp->cmd_code;
  1066. !         break;
  1067.       }
  1068.       }
  1069.   
  1070. --- 577,593 ----
  1071.       if (t < 0)
  1072.           j = k-1;
  1073.       else {
  1074. !         switch (restriction) {
  1075. !          case K_ONLY_MENU:
  1076. !          case K_ONLY_MORE:
  1077. !         if (cnmp->cmd_restriction == restriction) break;
  1078. !          case 0:
  1079. !         if (cnmp->cmd_restriction == 0) break;
  1080. !         return K_INVALID;
  1081. !          default:
  1082. !         break;
  1083. !         }
  1084. !         return cnmp->cmd_code;
  1085.       }
  1086.       }
  1087.   
  1088. *** ./LAST/man/nn.1.B    Mon Jul  9 17:59:57 1990
  1089. --- man/nn.1.B    Mon Jul 16 15:53:11 1990
  1090. ***************
  1091. *** 178,183 ****
  1092. --- 178,190 ----
  1093.   list of newsgroups to post to (you cannot enter a space because
  1094.   .B space
  1095.   is used for group name completion as described below).
  1096. +   If you enter \fB?\fP {\fBhelp-key\fP} as the first key, \fInn\fP
  1097. + will show you a list of all available news groups and their purpose.
  1098. + While paging through this list, you can enter \fBq\fP to quit looking
  1099. + at the list.  You can also enter \fB/\fP followed by a regular
  1100. + expression (typically a single word) which will cause \fInn\fP to show
  1101. + a (much shorter) list containing only the lines matching the regular
  1102. + expression.
  1103.   .LP
  1104.   Generally, \fInn\fP will construct a file with a suitable header, optionally
  1105.   include a copy of the article in the file with each non-empty line
  1106. ***************
  1107. *** 793,798 ****
  1108. --- 800,811 ----
  1109.   \fB:mkdir\fP [ \fIdirectory\fP ]
  1110.   Create the directory (and the directories in its path).  It will
  1111.   prompt for at directory name if the argument is omitted.
  1112. + .TP
  1113. + \fB:motd\fP
  1114. + Show the \fImessage of the day\fP (maintained by the news
  1115. + administrator in the file "motd" in the lib directory.  This file is
  1116. + automatically displayed on start-up whenever it changes if the
  1117. + \fBmotd\fP variable is set.
  1118.   .TP
  1119.   \fB:pwd\fP
  1120.   Print path name of current working directory on message line.
  1121. *** ./LAST/man/nn.1.C    Mon Jul  9 17:59:58 1990
  1122. --- man/nn.1.C    Mon Jul 16 15:53:11 1990
  1123. ***************
  1124. *** 661,666 ****
  1125. --- 661,672 ----
  1126.   characters in the received messages using a "cat -v" like format.
  1127.   Otherwise, only the printable characters are shown (default).
  1128.   .TP
  1129. + \fBmotd\fP        (boolean, default true)
  1130. + When set, \fInn\fP will display the \fImessage of the day\fP on
  1131. + start-up if it has changed since it was last shown.  The message is
  1132. + taken from the file "motd" in the lib directory.  It can also be shown
  1133. + (again) using the \fB:motd\fP command.
  1134. + .TP
  1135.   \fBnew-group-action\fP \fIaction\fP    (integer, default 3)
  1136.   This variable controls how new groups are treated by \fInn\fP.  It is
  1137.   an integer variable, and the following values can be used.  Some of
  1138. *** ./LAST/man/nn.1.D    Mon Jul  9 17:59:59 1990
  1139. --- man/nn.1.D    Mon Jul 16 15:53:11 1990
  1140. ***************
  1141. *** 34,44 ****
  1142. --- 34,48 ----
  1143.   .br
  1144.   \fBB\fP    Distribution:
  1145.   .br
  1146. + \fBC\fP    Control:
  1147. + .br
  1148.   \fBD\fP    Date:
  1149.   .br
  1150.   \fBd\fP    Date-Received:
  1151.   .br
  1152.   \fBF\fP    From:
  1153. + .br
  1154. + \fBf\fP    Sender:
  1155.   .br
  1156.   \fBG\fP    Newsgroup:    (current group)
  1157.   .br
  1158. *** ./LAST/man/nnacct.1m    Tue Apr 24 00:58:50 1990
  1159. --- man/nnacct.1m    Thu Jul 12 15:22:42 1990
  1160. ***************
  1161. *** 6,11 ****
  1162. --- 6,13 ----
  1163.   \fBnnacct\fP \-\fBr\fP [ \-\fBf\fP file ] [ \-\fBa\fP ] [ user ]...
  1164.   .br
  1165.   \fBnnacct\fP \-\fBp\fP\fIpolicy\fP \-\fBq\fP\fIquota\fP user...
  1166. + .br
  1167. + \fBnnacct\fP \-\fBZERO\fP
  1168.   .SH DESCRIPTION
  1169.   The \fInnacct\fP command provides an optional accounting and access
  1170.   authorization for news reading via the \fInn\fP news reader.
  1171. ***************
  1172. *** 26,31 ****
  1173. --- 28,38 ----
  1174.   given user is not already known in the accounting file, a new entry
  1175.   with the specified policy and quota is created (default values are
  1176.   used if both are not specified).
  1177. + .LP
  1178. + The third form (\-\fBZERO\fP) will clear the usage counts for
  1179. + \fIall\fP users.
  1180. + Individual usage counts cannot be cleared.
  1181. + The original accounting file is saved with a .old suffix.
  1182.   .LP
  1183.   The following policies are currently implemented:
  1184.   .TP
  1185. *** ./LAST/man/nngoback.1    Wed May 16 11:23:49 1990
  1186. --- man/nngoback.1    Tue Jul 10 22:13:31 1990
  1187. ***************
  1188. *** 61,66 ****
  1189. --- 61,67 ----
  1190.   Mark the articles which have arrived during the last week as unread.
  1191.   .LP
  1192.   You cannot go more than 14 days back with \fInngoback\fP.
  1193. + (You can change this limit as described below.)
  1194.   .SH THE BACK_ACT DAEMON
  1195.   It is a prerequisite for the use of \fInngoback\fP that the script
  1196.   \fBback_act\fP is executed at an appropriate time once (and only once)
  1197. ***************
  1198. *** 67,72 ****
  1199. --- 68,78 ----
  1200.   every day.  Preferably this is done by \fBcron\fP right before the
  1201.   bacth of news for `today' is received.  \fBback_act\fP will maintain
  1202.   copies of the active file for the last 14 days.
  1203. + .LP
  1204. + Optionally, the \fBback_act\fP program accepts a single numerical
  1205. + argument specifying how many copies of the active file it should
  1206. + maintain.  This is useful if news is expired after 7 days, in which
  1207. + case keeping more than 7 days of active file copies is wasteful.
  1208.   .SH FILES
  1209.   .DT
  1210.   .ta \w'~/.newsrc.goback'u+5m
  1211. *** ./LAST/man/nnpost.1    Tue Apr 24 17:31:39 1990
  1212. --- man/nnpost.1    Wed Jul 11 23:49:04 1990
  1213. ***************
  1214. *** 21,26 ****
  1215. --- 21,35 ----
  1216.   of the article.  Each of these prompts can also be supplied via
  1217.   command line options or arguments as described below.
  1218.   .LP
  1219. + When prompted for the "Newsgroup:", entering a \fB?\fP as the first
  1220. + key will cause \fInnpost\fP to list all the known news groups and
  1221. + their purpose (if this information is available).  You can also enter
  1222. + \fB/\fP followed by a word or regular expression which will cause
  1223. + \fInnpost\fP to produce a (much) shorter listing only containing the
  1224. + groups whose name and/or purpose description matches the regular
  1225. + expression.  When paging through either list, you can enter \fBq\fP to
  1226. + quit the listing.
  1227. + .LP
  1228.   If a source file is specified with \fB\-f\fP it will be used as the
  1229.   initial article body.  If the \fB\-p\fP option is also specified, the
  1230.   article is posted directly without editing.
  1231. *** ./LAST/menu.c    Mon Jul  9 18:00:01 1990
  1232. --- menu.c    Mon Jul 16 11:49:34 1990
  1233. ***************
  1234. *** 66,72 ****
  1235.   
  1236.   char attributes[30] = " .,+=#! **"; /* Corresponds to A_XXXX in data.h */
  1237.   
  1238. ! static prt_replies(level)
  1239.   {
  1240.       int re;
  1241.   
  1242. --- 66,72 ----
  1243.   
  1244.   char attributes[30] = " .,+=#! **"; /* Corresponds to A_XXXX in data.h */
  1245.   
  1246. ! prt_replies(level)
  1247.   {
  1248.       int re;
  1249.   
  1250. ***************
  1251. *** 606,635 ****
  1252.   static get_purpose(purpose)
  1253.   char *purpose;
  1254.   {
  1255. !     static FILE *f = NULL;
  1256. !     static not_avail = 0;
  1257. !     char line[256], group[80];
  1258.       register char *cp, *pp;
  1259.       register int len;
  1260.   
  1261. !     if (not_avail) return;
  1262. ! #ifdef NNTP
  1263. !     if (use_nntp) {
  1264. !     extern FILE *nntp_get_newsgroups();
  1265. !     f = nntp_get_newsgroups();
  1266. !     } else
  1267. ! #endif
  1268. !     f = open_file(relative(news_lib_directory, "newsgroups"), OPEN_READ);
  1269. !     if (f == NULL) {
  1270. !     not_avail = 1;
  1271. !     return;
  1272. !     }
  1273. !     rewind(f);
  1274.       
  1275. !     sprintf(group, "%s\t", current_group->group_name);
  1276. !     len = current_group->group_name_length + 1;
  1277.   
  1278.       while (fgets(line, 256, f) != NULL) {
  1279.       if (strncmp(line, group, len)) continue;
  1280.       cp = line + len;
  1281.       while (*cp && isspace(*cp)) cp++;
  1282. --- 606,627 ----
  1283.   static get_purpose(purpose)
  1284.   char *purpose;
  1285.   {
  1286. !     FILE *f, *open_purpose_file();
  1287. !     char line[256], *group;
  1288.       register char *cp, *pp;
  1289.       register int len;
  1290.   
  1291. !     if (current_group == NULL) return;
  1292. !     if ((current_group->master_flag & M_VALID) == 0) return;
  1293. !     if (current_group->group_flag & G_FAKED) return;
  1294.       
  1295. !     if ((f = open_purpose_file()) == NULL) return;
  1296. !     group = current_group->group_name;
  1297. !     len = current_group->group_name_length;
  1298.   
  1299.       while (fgets(line, 256, f) != NULL) {
  1300. +     if (!isascii(line[len]) || !isspace(line[len])) continue;
  1301.       if (strncmp(line, group, len)) continue;
  1302.       cp = line + len;
  1303.       while (*cp && isspace(*cp)) cp++;
  1304. ***************
  1305. *** 970,976 ****
  1306.   
  1307.            if (cur_k_cmd == K_CANCEL) {
  1308.                if (current_group->group_flag & G_FOLDER) {
  1309. !              if (ah->attr != A_CANCEL) fcancel(ah);
  1310.                } else
  1311.                switch (cancel(ah)) {
  1312.                 case -1:
  1313. --- 962,968 ----
  1314.   
  1315.            if (cur_k_cmd == K_CANCEL) {
  1316.                if (current_group->group_flag & G_FOLDER) {
  1317. !              fcancel(ah);
  1318.                } else
  1319.                switch (cancel(ah)) {
  1320.                 case -1:
  1321. *** ./LAST/more.c    Mon Jul  9 18:00:03 1990
  1322. --- more.c    Mon Jul 16 11:49:34 1990
  1323. ***************
  1324. *** 25,30 ****
  1325. --- 25,31 ----
  1326.   export int  expired_msg_delay = 1;
  1327.   export char *trusted_escapes = NULL;
  1328.   export int  new_read_prompt = 1;
  1329. + export int  re_layout_more = -1;
  1330.   
  1331.   import int  preview_window;
  1332.   import int  novice;
  1333. ***************
  1334. *** 70,77 ****
  1335. --- 71,80 ----
  1336.   } header_defs[] = {
  1337.       'A', "Approved",    &news.ng_appr,        0,
  1338.       'B', "Distribution",&news.ng_dist,        0,
  1339. +     'C', "Control",    &news.ng_control,    0,
  1340.       'D', "Date",    &news.ng_date,        &digest.dg_date,
  1341.       'F', "From",    &news.ng_from,        &digest.dg_from,
  1342. +     'f', "Sender",    &news.ng_sender,    0,
  1343.       'I', "Message-Id",    &news.ng_ident,        0,
  1344.       'K', "Keywords",    &news.ng_keyw,        0,
  1345.       'L', "Lines",    &news.ng_xlines,    0,
  1346. ***************
  1347. *** 320,326 ****
  1348. --- 323,343 ----
  1349.       if (preview_window < 1 && Lines - screen_offset < min_pv_window)
  1350.           screen_offset = 0;
  1351.           else {
  1352. + #ifdef notdef
  1353.           so_printxy(0, screen_offset++, "%s: %s ", ah->sender, ah->subject);
  1354. + #else
  1355. +         import int re_layout;
  1356. +         int o_re_layout;
  1357. +         so_gotoxy(0, screen_offset++, 0);
  1358. +         so_printf("%s: ", ah->sender);
  1359. +         o_re_layout = re_layout;
  1360. +         if (re_layout_more >= 0) re_layout = re_layout_more;
  1361. +         prt_replies(ah->replies);
  1362. +         re_layout = o_re_layout;
  1363. +         so_printf("%s", ah->subject);
  1364. +         so_end();
  1365. + #endif
  1366.           if (!STANDOUT) screen_offset++;
  1367.           clrline();
  1368.       }
  1369. *** ./LAST/news.c    Tue Jun 12 11:46:57 1990
  1370. --- news.c    Tue Jul 10 22:29:43 1990
  1371. ***************
  1372. *** 128,133 ****
  1373. --- 128,138 ----
  1374.       check("ack-References:", 15, ng_bref);
  1375.       break;
  1376.   
  1377. +      case 'C':
  1378. +      case 'c':
  1379. +     check("ontrol:",     7, ng_control);
  1380. +     break;
  1381.        case 'D':
  1382.        case 'd':
  1383.       check("ate:",          4, ng_date);
  1384. ***************
  1385. *** 193,200 ****
  1386.        case 'S':
  1387.        case 's':
  1388.       check("ubject:",     7, ng_subj);
  1389. !     if (news.ng_from == NULL)
  1390. !         check("ender:",     6, ng_from);
  1391.       if (!all) break;
  1392.       check("ummary:",     7, ng_summ);
  1393.       break;
  1394. --- 198,204 ----
  1395.        case 'S':
  1396.        case 's':
  1397.       check("ubject:",     7, ng_subj);
  1398. !     check("ender:",         6, ng_sender);
  1399.       if (!all) break;
  1400.       check("ummary:",     7, ng_summ);
  1401.       break;
  1402. ***************
  1403. *** 271,276 ****
  1404. --- 275,281 ----
  1405.       news.ng_groups     = NULL;
  1406.       news.ng_ref     = NULL;
  1407.       news.ng_bref     = NULL;
  1408. +     news.ng_sender    = NULL;
  1409.   
  1410.       news.ng_xlines     = NULL;
  1411.   
  1412. ***************
  1413. *** 284,289 ****
  1414. --- 289,295 ----
  1415.           news.ng_org     = NULL;
  1416.           news.ng_appr     = NULL;
  1417.           news.ng_summ    = NULL;
  1418. +         news.ng_control    = NULL;
  1419.           news.ng_date    = NULL;
  1420.           news.ng_rdate    = NULL;
  1421.       }
  1422. ***************
  1423. *** 294,299 ****
  1424. --- 300,306 ----
  1425.       body = parse_header(f, art_hdr_field, modes, buffer1);
  1426.   
  1427.       news.ng_lines = news.ng_xlines ? atoi(news.ng_xlines) : -1;
  1428. +     if (news.ng_from == NULL) news.ng_from = news.ng_sender;
  1429.   
  1430.       if (modes & FILL_OFFSETS) {
  1431.           art->fpos = news.ng_fpos = ftell(f);
  1432. *** ./LAST/news.h    Wed May  2 00:03:37 1990
  1433. --- news.h    Tue Jul 10 22:29:43 1990
  1434. ***************
  1435. *** 27,32 ****
  1436. --- 27,34 ----
  1437.       char    *ng_org;    /*   organization        */
  1438.       char    *ng_appr;    /*   approved            */
  1439.       char    *ng_summ;    /*   summary            */
  1440. +     char    *ng_control;    /*   control            */
  1441. +     char    *ng_sender;    /*   sender            */
  1442.   
  1443.       char    *ng_date;    /*   date            */
  1444.   
  1445. *** ./LAST/newsrc.c    Mon Jun 25 15:46:50 1990
  1446. --- newsrc.c    Thu Jul 12 17:52:14 1990
  1447. ***************
  1448. *** 184,189 ****
  1449. --- 184,190 ----
  1450.       FILE *lf = NULL;
  1451.       char buf[FILENAME];
  1452.       register int i;
  1453. +     time_t t;
  1454.   
  1455.       if (new_group_action == RCX_RNLAST) {
  1456.       rnlast_path = home_relative(".rnlast");
  1457. ***************
  1458. *** 195,204 ****
  1459.           rnlast_line[i] = copy_str(buf);
  1460.       }
  1461.       if (i != MAX_RNLAST_LINE) {
  1462. !         printf(".rnlast only supported with active.times patches\n");
  1463. !         sleep(3);
  1464. !         new_group_action = RCX_TIME_CONF;
  1465. !         goto no_file;
  1466.       }
  1467.       fclose(lf);
  1468.       return (time_t)atol(rnlast_line[RN_LAST_CREATION_TIME]);
  1469. --- 196,207 ----
  1470.           rnlast_line[i] = copy_str(buf);
  1471.       }
  1472.       if (i != MAX_RNLAST_LINE) {
  1473. !         while (i <= MAX_RNLAST_LINE) {
  1474. !         rnlast_line[i] = copy_str("");
  1475. !         i++;
  1476. !         }
  1477. !         fclose(lf);
  1478. !         return (time_t)atol(rnlast_line[RN_LAST_TIME_RUN]);
  1479.       }
  1480.       fclose(lf);
  1481.       return (time_t)atol(rnlast_line[RN_LAST_CREATION_TIME]);
  1482. ***************
  1483. *** 213,219 ****
  1484.   
  1485.    no_file:
  1486.       if (lf != NULL) fclose(lf);
  1487. !     return (time_t)(-1);
  1488.   }
  1489.   
  1490.   static update_last_new(lastg)
  1491. --- 216,223 ----
  1492.   
  1493.    no_file:
  1494.       if (lf != NULL) fclose(lf);
  1495. !     t = file_exist(newsrc_file, (char *)NULL);
  1496. !     return t > 0 ? t : (time_t)(-1);
  1497.   }
  1498.   
  1499.   static update_last_new(lastg)
  1500. ***************
  1501. *** 396,403 ****
  1502.            case RCX_TIME:
  1503.            case RCX_TIME_CONF:
  1504.            case RCX_RNLAST:
  1505. !         if (last_new_group == 0)
  1506.               last_new_group = get_last_new();
  1507.   
  1508.           if (gh->creation_time <= last_new_group) {
  1509.               /* old groups not in .newsrc are unsubscribed */
  1510. --- 400,415 ----
  1511.            case RCX_TIME:
  1512.            case RCX_TIME_CONF:
  1513.            case RCX_RNLAST:
  1514. !         if (last_new_group == 0) {
  1515.               last_new_group = get_last_new();
  1516. +             if (last_new_group < 0 && new_group_action != RCX_RNLAST) {
  1517. +             /* maybe this is a first time rn convert ? */
  1518. +             int nga = new_group_action;
  1519. +             new_group_action = RCX_RNLAST;
  1520. +             last_new_group = get_last_new();
  1521. +             new_group_action = nga;
  1522. +             }
  1523. +         }
  1524.   
  1525.           if (gh->creation_time <= last_new_group) {
  1526.               /* old groups not in .newsrc are unsubscribed */
  1527. *** ./LAST/nn.c    Mon Jul  9 18:00:03 1990
  1528. --- nn.c    Thu Jul 12 18:43:28 1990
  1529. ***************
  1530. *** 32,37 ****
  1531. --- 32,38 ----
  1532.   export int
  1533.       article_limit = -1,
  1534.       also_read_articles = 0,
  1535. +     batch_mode = 0,
  1536.       conf_auto_quit = 0,
  1537.       do_kill_handling = 1,
  1538.       group_name_args = 0,
  1539. ***************
  1540. *** 38,43 ****
  1541. --- 39,45 ----
  1542.       merged_menu = 0,
  1543.       repeat_group_query = 0,
  1544.       report_cost_on_exit = 1,
  1545. +     show_motd_on_entry = 1,
  1546.       silent = 0,
  1547.       verbose = 0,
  1548.       Debug = 0;
  1549. ***************
  1550. *** 529,534 ****
  1551. --- 531,571 ----
  1552.       return 0;
  1553.   }
  1554.   
  1555. + static prt_version()
  1556. + {
  1557. +     printf("Release %s,  Kim F. Storm, 1990\n\n", version_id);
  1558. + }
  1559. + display_motd(check)
  1560. + int check;
  1561. + {
  1562. +     time_t last_motd, cur_motd;
  1563. +     char *dot_motd, motd[FILENAME];
  1564. +     strcpy(motd, relative(lib_directory, "motd"));
  1565. +     cur_motd = file_exist(motd, (char *)NULL);
  1566. +     if (cur_motd == 0) return 0;
  1567. +     if (!check) {
  1568. +     display_file(motd, CLEAR_DISPLAY | CONFIRMATION);
  1569. +     return 1;
  1570. +     }
  1571. +     dot_motd = relative(nn_directory, ".motd");
  1572. +     last_motd = file_exist(dot_motd, (char *)NULL);
  1573. +     if (last_motd >= cur_motd) return 0;
  1574. +     fclose(open_file(dot_motd, OPEN_CREATE|MUST_EXIST));
  1575. +     clrdisp();
  1576. +     so_printxy(0, 0, "Message of the day");
  1577. +     gotoxy(0, 2); prt_version();
  1578. +     display_file(motd, CONFIRMATION);
  1579. +     return 1;
  1580. + }
  1581.   
  1582.   clean_group(gh)
  1583.   group_header *gh;
  1584. ***************
  1585. *** 583,594 ****
  1586.       } else
  1587.       if (strcmp(pname, "nnpost") == 0) {
  1588.       who_am_i = I_AM_POST;
  1589.       } else {
  1590.       who_am_i = I_AM_NN;
  1591.       }
  1592.   
  1593.       if (who_am_i == I_AM_NN || (who_am_i == I_AM_ADMIN && argc == 1)) {
  1594. !     if (!isatty(0)) {
  1595.           if (who_am_i == I_AM_NN && argc == 2) {
  1596.           if (strcmp(argv[1], "-SPEW") == 0) {
  1597.               who_am_i = I_AM_SPEW;
  1598. --- 620,635 ----
  1599.       } else
  1600.       if (strcmp(pname, "nnpost") == 0) {
  1601.       who_am_i = I_AM_POST;
  1602. +     } else
  1603. +     if (strcmp(pname, "nnbatch") == 0) {
  1604. +     who_am_i = I_AM_NN;
  1605. +     batch_mode = 1;
  1606.       } else {
  1607.       who_am_i = I_AM_NN;
  1608.       }
  1609.   
  1610.       if (who_am_i == I_AM_NN || (who_am_i == I_AM_ADMIN && argc == 1)) {
  1611. !     if (!batch_mode && !isatty(0)) {
  1612.           if (who_am_i == I_AM_NN && argc == 2) {
  1613.           if (strcmp(argv[1], "-SPEW") == 0) {
  1614.               who_am_i = I_AM_SPEW;
  1615. ***************
  1616. *** 632,638 ****
  1617.   #endif
  1618.   
  1619.       if ((say_welcome = init_global()) < 0) {
  1620. !     printf("%s: nn must been invoked to initialize user files\n", pname);
  1621.       nn_exit(1);
  1622.       }
  1623.   
  1624. --- 673,679 ----
  1625.   #endif
  1626.   
  1627.       if ((say_welcome = init_global()) < 0) {
  1628. !     printf("%s: nn has not been invoked to initialize user files\n", pname);
  1629.       nn_exit(1);
  1630.       }
  1631.   
  1632. ***************
  1633. *** 661,666 ****
  1634. --- 702,712 ----
  1635.       }
  1636.   
  1637.       visit_init_file(0, argv[1]);
  1638. +     if (show_motd_on_entry)
  1639. +         if (display_motd(1))
  1640. +         silent = 1;
  1641. +     
  1642.       init_answer();
  1643.   
  1644.       group_name_args =
  1645. ***************
  1646. *** 762,768 ****
  1647.       }
  1648.   
  1649.       if (!silent && who_am_i != I_AM_CHECK)
  1650. !     printf("Release %s,  Kim F. Storm, 1990\n\n", version_id);
  1651.   
  1652.       if (nn_locked()) nn_exit(2);
  1653.   
  1654. --- 808,814 ----
  1655.       }
  1656.   
  1657.       if (!silent && who_am_i != I_AM_CHECK)
  1658. !     prt_version();
  1659.   
  1660.       if (nn_locked()) nn_exit(2);
  1661.   
  1662. *** ./LAST/patchlevel.h    Mon Jul  9 18:00:04 1990
  1663. --- patchlevel.h    Mon Jul 16 12:22:43 1990
  1664. ***************
  1665. *** 18,24 ****
  1666.    *    1990-06-11: Patch #5 (6.4.5) - MEDIUM
  1667.    *    1990-06-25: Patch #6 (6.4.6) - MEDIUM
  1668.    *    1990-07-09: Patch #7 (6.4.7) - LOW
  1669.    */
  1670.   
  1671. ! #define PATCHLEVEL 7
  1672.   
  1673. --- 18,25 ----
  1674.    *    1990-06-11: Patch #5 (6.4.5) - MEDIUM
  1675.    *    1990-06-25: Patch #6 (6.4.6) - MEDIUM
  1676.    *    1990-07-09: Patch #7 (6.4.7) - LOW
  1677. +  *    1990-07-16: Patch #8 (6.4.8) - HIGH
  1678.    */
  1679.   
  1680. ! #define PATCHLEVEL 8
  1681.   
  1682. *** ./LAST/prefix.c    Wed May  2 21:19:42 1990
  1683. --- prefix.c    Thu Jul 12 19:27:22 1990
  1684. ***************
  1685. *** 74,79 ****
  1686. --- 74,84 ----
  1687.       fprintf(f, "MASTER=%s\n", master_directory);
  1688.       fprintf(f, "HELP=%s\n", help_directory);
  1689.       fprintf(f, "DBDATA=\"%s\"\n", db_data_directory ? db_data_directory : "");
  1690. + #ifdef DB_LONG_NAMES
  1691. +     fprintf(f, "DBSHORTNAME=false\n");
  1692. + #else
  1693. +     fprintf(f, "DBSHORTNAME=true\n");
  1694. + #endif
  1695.       fprintf(f, "OWNER=%s%c", OWNER, nl);
  1696.       fprintf(f, "GROUP=%s\n", GROUP);
  1697.       }
  1698. *** ./LAST/save.c    Mon Jul  9 18:00:05 1990
  1699. --- save.c    Tue Jul 10 10:32:30 1990
  1700. ***************
  1701. *** 508,514 ****
  1702.       } else
  1703.       if (mode == SHORT_HEADER || mode == SHORT_HEADER_DG) {
  1704.       char *name, *val;
  1705. !     scan_header_fields(short_header_lines, ah->flag & A_DIGEST);
  1706.       while (next_header_field(&name, &val, (fct_type *)NULL)) {
  1707.           if (name == NULL) continue;
  1708.           fprintf(save_file, "%s: %s\n", name, val);
  1709. --- 508,514 ----
  1710.       } else
  1711.       if (mode == SHORT_HEADER || mode == SHORT_HEADER_DG) {
  1712.       char *name, *val;
  1713. !     scan_header_fields(short_header_lines, ah);
  1714.       while (next_header_field(&name, &val, (fct_type *)NULL)) {
  1715.           if (name == NULL) continue;
  1716.           fprintf(save_file, "%s: %s\n", name, val);
  1717. *** ./LAST/term.c    Mon Jun 25 15:46:52 1990
  1718. --- term.c    Wed Jul 11 21:58:11 1990
  1719. ***************
  1720. *** 9,14 ****
  1721. --- 9,15 ----
  1722.   #include "config.h"
  1723.   #include "term.h"
  1724.   #include "keymap.h"
  1725. + #include "regexp.h"
  1726.   
  1727.   #ifdef RESIZING
  1728.   #include <sys/ioctl.h>            /* for TIOCGWINSZ */
  1729. ***************
  1730. *** 21,26 ****
  1731. --- 22,28 ----
  1732.   #endif
  1733.   
  1734.   import int data_bits;
  1735. + import int batch_mode;
  1736.   
  1737.   struct msg_list {
  1738.       char *buf;
  1739. ***************
  1740. *** 313,318 ****
  1741. --- 315,325 ----
  1742.       char tbuf[1024];
  1743.   #endif
  1744.   
  1745. +     if (batch_mode) {
  1746. +     term_name = "batch";
  1747. +     return;
  1748. +     }
  1749.       if ((term_name = getenv("TERM")) == NULL)
  1750.       user_error("No TERM variable in environment");
  1751.   
  1752. ***************
  1753. *** 845,850 ****
  1754. --- 852,860 ----
  1755.           return K_interrupt;
  1756.       }
  1757.   
  1758. +     if (batch_mode)
  1759. +     user_error("Attempt to read keyboard input in batch mode");
  1760.       for (i = multi_keys, mk = multi_key_list; --i >= 0; mk++)
  1761.       mk->cur_key = mk->keys;
  1762.       key_cnt = 0;
  1763. ***************
  1764. *** 1332,1338 ****
  1765.   
  1766.   chain:
  1767.   
  1768. !     f = open_file(relative(help_directory, name), OPEN_READ);
  1769.       if (f == NULL)
  1770.       printf("\r\n\nFile %s is not available\n\n", name);
  1771.       else {
  1772. --- 1342,1349 ----
  1773.   
  1774.   chain:
  1775.   
  1776. !     if (*name != '/') name = relative(help_directory, name);
  1777. !     f = open_file(name, OPEN_READ);
  1778.       if (f == NULL)
  1779.       printf("\r\n\nFile %s is not available\n\n", name);
  1780.       else {
  1781. ***************
  1782. *** 1650,1659 ****
  1783. --- 1661,1678 ----
  1784.   
  1785.   
  1786.   static pg_fline, pg_width, pg_maxw, pg_line, pg_col, pg_quit;
  1787. + export regexp *pg_regexp = NULL;
  1788. + export int pg_new_regexp = 0;
  1789.   
  1790.   pg_init(first_line, cols)
  1791.   int first_line, cols;
  1792.   {
  1793. +     if (pg_regexp) {
  1794. +     freeobj(pg_regexp);
  1795. +     pg_regexp = NULL;
  1796. +     }
  1797. +     pg_new_regexp = 0;
  1798.       pg_fline = first_line;
  1799.       pg_line = pg_fline - 1;
  1800.       pg_quit = pg_col = 0;
  1801. ***************
  1802. *** 1684,1689 ****
  1803. --- 1703,1720 ----
  1804.       gotoxy(pg_col, pg_line);
  1805.       if (pg_line == Lines - 1 && pg_col == pg_maxw) {
  1806.           c = any_key(0);
  1807. +         if (c == '/') {
  1808. +         char *expr;
  1809. +         putchar(CR);
  1810. +         putchar('/');
  1811. +         clrline();
  1812. +         expr = get_s((char *)NULL, (char *)NULL, (char *)NULL, NULL_FCT);
  1813. +         if (expr != NULL && *expr != NUL) {
  1814. +             freeobj(pg_regexp);
  1815. +             pg_regexp = regcomp(expr);
  1816. +             pg_new_regexp = 1;
  1817. +         }
  1818. +         }
  1819.           gotoxy(0, pg_fline);
  1820.           clrpage(pg_fline);
  1821.           pg_col = 0;
  1822. ***************
  1823. *** 1712,1717 ****
  1824. --- 1743,1753 ----
  1825.   {
  1826.       if (pg_quit == 0 && pg_next() == 0)
  1827.       any_key(0);
  1828. +     if (pg_regexp) {
  1829. +     freeobj(pg_regexp);
  1830. +     pg_regexp = NULL;
  1831. +     }
  1832.   }
  1833.   
  1834.   
  1835. *** ./LAST/variable.c    Mon Jul  9 18:00:05 1990
  1836. --- variable.c    Mon Jul 16 12:09:36 1990
  1837. ***************
  1838. *** 6,11 ****
  1839. --- 6,12 ----
  1840.   
  1841.   #include "config.h"
  1842.   #include "keymap.h"
  1843. + #include "regexp.h"
  1844.   
  1845.   import in_init;
  1846.   
  1847. ***************
  1848. *** 109,114 ****
  1849. --- 110,116 ----
  1850.       shell_restrictions,
  1851.       show_article_date,
  1852.       show_current_time,
  1853. +     show_motd_on_entry,
  1854.       silent,
  1855.       slow_mode,
  1856.       suggest_save_file,
  1857. ***************
  1858. *** 149,154 ****
  1859. --- 151,157 ----
  1860.       preview_window,
  1861.       print_header_type,
  1862.       re_layout,
  1863. +     re_layout_more,
  1864.       response_check_pause,
  1865.       retry_on_error,
  1866.       save_counter_offset,
  1867. ***************
  1868. *** 178,183 ****
  1869. --- 181,187 ----
  1870.   #undef SPEC
  1871.   #undef SAFE
  1872.   #undef INIT
  1873. + #undef CODES
  1874.   
  1875.   
  1876.   #define    V_STRING    0x1000
  1877. ***************
  1878. *** 185,190 ****
  1879. --- 189,195 ----
  1880.   #define    V_INTEGER    0x3000
  1881.   #define V_KEY        0x4000
  1882.   #define    V_SPECIAL    0x5000
  1883. + #define V_CODES        0x6000
  1884.   
  1885.   #define V_SAFE        0x0100
  1886.   #define V_INIT        0x0200
  1887. ***************
  1888. *** 196,205 ****
  1889. --- 201,216 ----
  1890.   #define    INT        V_INTEGER |
  1891.   #define KEY        V_KEY |
  1892.   #define    SPEC        V_SPECIAL |
  1893. + #define    CODES        V_CODES |
  1894.   
  1895.   #define SAFE        V_SAFE |
  1896.   #define INIT        V_INIT |
  1897.   
  1898. + static char *code_strings[16] = {
  1899. +     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  1900. +     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
  1901. + };
  1902.   struct variable_defs {
  1903.       char *var_name;
  1904.       int  var_flags;
  1905. ***************
  1906. *** 289,294 ****
  1907. --- 300,306 ----
  1908.       "min-window",        INT 1,        (char **)&min_pv_window,
  1909.       "mmdf-format",        BOOL 0,        (char **)&use_mmdf_folders,
  1910.       "monitor",            BOOL 0,        (char **)&monitor_mode,
  1911. +     "motd",            BOOL 0,        (char **)&show_motd_on_entry,
  1912.       "new-group-action",        INT 0,        (char **)&new_group_action,
  1913.       "new-style-read-prompt",    BOOL 0,        (char **)&new_read_prompt,
  1914.       "news-header",        STR 0,        (char **)&extra_news_headers,
  1915. ***************
  1916. *** 315,320 ****
  1917. --- 327,333 ----
  1918.       "quick-count",        BOOL 0,        (char **)&quick_unread_count,
  1919.       "quick-save",        BOOL 0,        (char **)&quick_save,
  1920.       "re-layout",        INT 0,        (char **)&re_layout,
  1921. +     "re-layout-read",        INT 0,        (char **)&re_layout_more,
  1922.       "read-return-next-page",    BOOL 0,        (char **)&read_ret_next_page,
  1923.       "record",            SPEC 1,        (char **)NULL,
  1924.       "repeat",            BOOL 0,        (char **)&fmt_rptsubj,
  1925. ***************
  1926. *** 331,338 ****
  1927.       "scroll-clear-page",    BOOL 0,        (char **)&scroll_clear_page,
  1928.       "select-leave-next",    BOOL 0,        (char **)&select_leave_next,
  1929.       "select-on-sender",        BOOL 0,        (char **)&select_on_sender,
  1930. !     "shading-off",        STR 0,        (char **)&shade_off_attr,
  1931. !     "shading-on",        STR 0,        (char **)&shade_on_attr,
  1932.       "shell",            STR SAFE 0,    (char **)&user_shell,
  1933.       "shell-restrictions",     BOOL INIT 0,    (char **)&shell_restrictions,
  1934.       "show-purpose-mode",    INT 0,        (char **)&show_purpose_mode,
  1935. --- 344,351 ----
  1936.       "scroll-clear-page",    BOOL 0,        (char **)&scroll_clear_page,
  1937.       "select-leave-next",    BOOL 0,        (char **)&select_leave_next,
  1938.       "select-on-sender",        BOOL 0,        (char **)&select_on_sender,
  1939. !     "shading-off",        CODES 0,    (char **)&shade_off_attr,
  1940. !     "shading-on",        CODES 1,    (char **)&shade_on_attr,
  1941.       "shell",            STR SAFE 0,    (char **)&user_shell,
  1942.       "shell-restrictions",     BOOL INIT 0,    (char **)&shell_restrictions,
  1943.       "show-purpose-mode",    INT 0,        (char **)&show_purpose_mode,
  1944. ***************
  1945. *** 591,596 ****
  1946. --- 604,642 ----
  1947.           article_limit = (on && value > 0) ? value : -1;
  1948.           break;
  1949.       }
  1950. +      case V_CODES:
  1951. +     {
  1952. +         char codes[80], code[16], *sp, *cp, *vs;
  1953. +         if (val_string == NULL) on = 0;
  1954. +         if (on) {
  1955. +         adjust(val_string);
  1956. +         if (val_string[0] == NUL) on = 0;
  1957. +         }
  1958. +         if (on) {
  1959. +         sp = codes;
  1960. +         vs = val_string;
  1961. +         while (*vs) {
  1962. +             while (*vs && (!isascii(*vs) || isspace(*vs)))
  1963. +             vs++;
  1964. +             if (*vs == NUL) break;
  1965. +             cp = code;
  1966. +             while (*vs && isascii(*vs) && !isspace(*vs))
  1967. +             *cp++ = *vs++;
  1968. +             *cp = NUL;
  1969. +             *sp++ = parse_key(code);
  1970. +         }
  1971. +         *sp = NUL;
  1972. +         if (codes[0] == NUL) on = 0;
  1973. +         }
  1974. +         freeobj(code_strings[VAR_OP]);
  1975. +         code_strings[VAR_OP] = on ? copy_str(val_string) : NULL;
  1976. +         STR_VAR = on ? copy_str(codes) : (char *)NULL;
  1977. +         break;
  1978. +     }
  1979.       break;
  1980.       }
  1981.       return 0;
  1982. ***************
  1983. *** 652,657 ****
  1984. --- 698,707 ----
  1985.           break;
  1986.       }
  1987.       break;
  1988. +      case V_CODES:
  1989. +     str = code_strings[VAR_OP];
  1990. +     break;
  1991.       }
  1992.       if (str == NULL) str = "NULL";
  1993.       return str;
  1994. ***************
  1995. *** 843,848 ****
  1996. --- 893,902 ----
  1997.        case V_SPECIAL:
  1998.       msg("Cannot push pseudo variable %s", var->var_name);
  1999.       break;
  2000. +      case V_CODES:
  2001. +     msg("Cannot push code string variable %s", var->var_name);
  2002. +     break;
  2003.       }
  2004.   
  2005.       return 1;
  2006. ***************
  2007. *** 900,905 ****
  2008. --- 954,962 ----
  2009.   
  2010.        case V_SPECIAL:    /* these are not saved, so... */
  2011.           break;
  2012. +      case V_CODES:
  2013. +         break;
  2014.       }
  2015.   
  2016.       vs1 = vs->next;
  2017. ***************
  2018. *** 926,931 ****
  2019. --- 983,990 ----
  2020.       char *str, pushed;
  2021.       int b;
  2022.       register struct variable_defs *var;
  2023. +     extern regexp *pg_regexp;
  2024. +     extern int pg_new_regexp;
  2025.   
  2026.       if (in_init) return;
  2027.   
  2028. ***************
  2029. *** 940,995 ****
  2030.       so_printf("Variable settings:");
  2031.   
  2032.       for (var = variables; var < &variables[TABLE_SIZE]; var++) {
  2033. !     pushed =
  2034. !         var_on_stack(var) ? '>' :
  2035. !         (var->var_flags & V_MODIFIED) ? '*' : ' ';
  2036.       if (!all && pushed == ' ') continue;
  2037. !     switch (VAR_TYPE) {
  2038. !      case V_STRING:
  2039. !         if (pg_next() < 0) goto out;
  2040. !         str = (VAR_OP == 1) ? CBUF_VAR : STR_VAR;
  2041. !         if (str == NULL) str = "";
  2042. !         printf("%c %-20.20s = \"%s\"\n", pushed, var->var_name, str);
  2043. !         break;
  2044. !      case V_BOOLEAN:
  2045. !         if (pg_next() < 0) goto out;
  2046. !         b = BOOL_VAR;
  2047. !         if (VAR_OP == 2 || VAR_OP == 4) b = !b;
  2048. !         printf("%c %-20.20s = %s\n",
  2049. !            pushed, var->var_name, b ? "on" : "off");
  2050. !         break;
  2051. !      case V_INTEGER:
  2052. !         if (pg_next() < 0) goto out;
  2053. !         printf("%c %-20.20s = %d\n", pushed, var->var_name, INT_VAR);
  2054. !         break;
  2055. !      case V_KEY:
  2056. !         if (pg_next() < 0) goto out;
  2057. !         printf("%c %-20.20s = %s\n",
  2058. !            pushed, var->var_name, key_name(KEY_VAR));
  2059. !         break;
  2060. !      case V_SPECIAL:
  2061. !         switch (VAR_OP) {
  2062. !          case 1:
  2063. !         break;
  2064. !          case 2:
  2065. !         if (also_read_articles) {
  2066. !             if (pg_next() < 0) goto out;
  2067. !             printf("%c %-20.20s = %d\n",
  2068. !                pushed, var->var_name, article_limit);
  2069. !         }
  2070. !         break;
  2071. !         }
  2072. !         break;
  2073.       }
  2074.       }
  2075.   
  2076. - out:
  2077.       pg_end();
  2078.   }
  2079.   
  2080. --- 999,1021 ----
  2081.       so_printf("Variable settings:");
  2082.   
  2083.       for (var = variables; var < &variables[TABLE_SIZE]; var++) {
  2084. !     if (pg_regexp != NULL && regexec(pg_regexp, var->var_name) == 0)
  2085. !         continue;
  2086. !     str = var_value(var, &pushed);
  2087.       if (!all && pushed == ' ') continue;
  2088. !     if (pg_next() < 0) break;
  2089. !     if (pg_new_regexp) {
  2090. !         pg_new_regexp = 0;
  2091. !         var = variables;
  2092. !         var--;
  2093. !         continue;
  2094.       }
  2095. +     if (VAR_TYPE == V_STRING)
  2096. +         printf("%c %-25.25s = \"%s\"\n", pushed, var->var_name, str);
  2097. +     else
  2098. +         printf("%c %-25.25s = %s\n", pushed, var->var_name, str);
  2099.       }
  2100.   
  2101.       pg_end();
  2102.   }
  2103.   
  2104.